package client;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.*;
import java.net.MalformedURLException;
import java.rmi.*;
import java.util.*;

import common.*;
import static common.Constants.*;
import static common.NotificationMsg.*;

public class IMClientGUI extends JDialog implements ActionListener{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;


	private static final String CHAT_ICON = "resource/chat.png";


	private JList userJLst;
	private JComboBox nameBox;
	private SortedUserListModel uList;
	private PrivateUser user;
	private JPanel loginPanel;
	private JPanel userPanel;
	private Thread updater;
	private final SysTray sysTray;
	private HashMap<Long,ChatWindow> activeChats;
	private UserConfiguration config;
	private JLabel userInfo = new JLabel();
	
	public IMClientGUI() {
		super();
		activeChats = new HashMap<Long,ChatWindow>();
		uList = new SortedUserListModel();
		nameBox = new JComboBox();
		
		JLabel nameLabel = new JLabel("Alias:");
		nameLabel.setLabelFor(nameBox);
		nameLabel.setAlignmentX(LEFT_ALIGNMENT);
		
		JButton loginBtn = new JButton("login");
		loginBtn.setActionCommand("login");
		loginBtn.addActionListener(this);
		loginBtn.setAlignmentX(LEFT_ALIGNMENT);
		
		JButton logoutBtn = new JButton("logout");
		logoutBtn.setActionCommand("logout");
		logoutBtn.addActionListener(this);
		
		userJLst = new JList(uList);
		userJLst.setCellRenderer(new UserRenderer());
		userJLst.addMouseListener(new ClientListMouseListener());
		userJLst.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		JScrollPane userJLstScroller = new JScrollPane(userJLst);
		userJLstScroller.setPreferredSize(new Dimension(130,220));
		
		loginPanel = new JPanel();
		loginPanel.setLayout(new BoxLayout(loginPanel, BoxLayout.Y_AXIS));
		loginPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
		loginPanel.add(nameLabel);
		loginPanel.add(nameBox);
		loginPanel.add(loginBtn);
		
		userInfo.addMouseListener(new MouseAdapter(){
			public void mouseClicked(MouseEvent e){
				showModifyWin();
			}
		});
		userInfo.setForeground(Color.GREEN);
		userInfo.setOpaque(true);
		userInfo.setBackground(Color.black);
		userInfo.setHorizontalAlignment(SwingConstants.CENTER);

		userPanel = new JPanel();
		userPanel.setLayout(new BorderLayout(0,5));
		userPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
		userPanel.add(userInfo,BorderLayout.PAGE_START);
		userPanel.add(userJLstScroller,BorderLayout.CENTER);
		userPanel.add(logoutBtn,BorderLayout.PAGE_END);
		
		sysTray = new SysTray(this);
		if(sysTray.getTrayIcon() != null)
			setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
		else {
			addWindowListener(new WindowAdapter() {
			      public void windowClosing(WindowEvent e) {
			    	 ((IMClientGUI)e.getSource()).logout(false);
			        System.exit(0);
			      }
			    });
		}
		
		setLoginVisible(true);
		setTitle("My IM Client");
		setIconImage(Utilities.createImage(CHAT_ICON, "chat icon"));
		setLocationByPlatform(true);
		setLocationRelativeTo(null);
		setVisible(true);
	}

	private void showModifyWin(){
		new InfoModifier(this);
	}
	
	private void loadUserProfiles() {
		File profileFolder = new File("profile");
		nameBox.removeAllItems();
		if(profileFolder.exists()&&profileFolder.isDirectory()){
			File[] profiles =profileFolder.listFiles(new FileListFilter("","xml"));
			Arrays.sort(profiles, new Comparator<File>(){
				public int compare(File f1, File f2){
					return Long.valueOf(f2.lastModified()).compareTo(f1.lastModified());
				}
			});
			for(File p : profiles){
				nameBox.addItem(p.getName().substring(0, p.getName().lastIndexOf(".")));
			}
		}
		nameBox.setEditable(true);
		nameBox.setAlignmentX(LEFT_ALIGNMENT);
		nameBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
	}

	private void loadConfig(String filename){
		File confFile = new File(filename);
		if(confFile.exists()){
			try {
				XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(new FileInputStream(confFile)));
				config = (UserConfiguration) decoder.readObject();
				decoder.close();
			} catch (FileNotFoundException e) {
			}
		} else{
			config = new UserConfiguration();
			config.setServerAddress(SERVER_ADDRESS);
			config.setServerPort(SERVER_PORT);
			config.setServerServiceName(RMI_SERVICE_NAME);
		}
	}
	
	private void saveConfig(String alias){
		try {
			config.setLastAlias(alias);
			if(user!=null){
				config.setLastId(user.getUserId());
				config.setKey(user.getKey());
			}
			if(!new File("profile").exists())
				new File("profile").mkdirs();
			XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("profile/"+alias+".xml")));
			encoder.writeObject(config);
			encoder.close();
		} catch (FileNotFoundException e) {
		}
	}
	
	private void createChatWindow(User c){
		if( c == null) return;
		if(!activeChats.containsKey(c.getUserId())){
			ServerInterface server = getServer();
			String[] fonts = null;
			if(server!=null)
				try {
					fonts = server.getSupportFonts(c.getUserId());
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			ChatWindow chatWin = new ChatWindow(this,c,fonts);
			activeChats.put(c.getUserId(), chatWin);
		}
		activeChats.get(c.getUserId()).showWindow();
	}
	
	private class ClientListMouseListener extends MouseAdapter{

		@Override
		public void mouseClicked(MouseEvent e) {
			if(e.getClickCount()==2){
				createChatWindow((User)userJLst.getSelectedValue());
			}
			
		}
	}
	
	private class StatusUpdater implements Runnable{
		public void run(){
			while(!Thread.interrupted()){
				try {
					ServerInterface server = getServer();
					if(server == null || user == null){
						SwingUtilities.invokeLater( new Runnable(){
							public void run(){
								logout(true);
							}
						});
						return;
					}
					processNotification(server.getNotification(user.getUserId(), user.getClientId()));
					Thread.sleep(1000);
				} catch (RemoteException e) {
					SwingUtilities.invokeLater( new Runnable(){
						public void run(){
							logout(true);
						}
					});
					return;
				} catch (InterruptedException e){
					return;
				}
			}
		}
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand().equals("login")) {
			if(nameBox.getSelectedItem()==null||nameBox.getSelectedItem().toString().length()<=0){
				JOptionPane.showMessageDialog(this, "Alias should not be empty!", "Oops!", JOptionPane.WARNING_MESSAGE);
				return;
			}
			String alias = nameBox.getSelectedItem().toString();
			if(config == null || config.getLastAlias() != alias){
				if(!new File("profile/"+alias+".xml").exists())
					nameBox.addItem(alias);
				loadConfig("profile/"+alias+".xml");
			}
			ServerInterface server = getServer();
			if (server == null) {
				saveConfig(alias);
				JOptionPane.showMessageDialog(this, "Cannot connect server!", "Oops!", JOptionPane.ERROR_MESSAGE);
				return;
			}
			try {
				user = server.login(config.getLastId(), alias, config.getKey());
				saveConfig(alias);
				server.setSupportFonts(user.getUserId(), user.getClientId(), GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
				userInfo.setText(user.toString());
				setLoginVisible(false);
				if(sysTray.getTrayIcon() != null){
					sysTray.getTrayIcon().setToolTip(user.toString());
					sysTray.getTrayIcon().setImage(Utilities.createImage(SysTray.OONLINE_TRAY_ICON, "online tray"));
				}
				updater = new Thread(new StatusUpdater());
				updater.start();
				
			} catch (RemoteException e1) {
				JOptionPane.showMessageDialog(this, "login failed!", "Oops!", JOptionPane.ERROR_MESSAGE);
				e1.printStackTrace();
			}
		}
		else if(e.getActionCommand().equals("logout")){
			logout(false);
		}

	}

	public void logout(boolean onError) {
		if(user == null) return;
		if(sysTray.getTrayIcon() != null){
			sysTray.getTrayIcon().setToolTip("My IM Client");
			sysTray.getTrayIcon().setImage(Utilities.createImage(SysTray.OFFLINE_TRAY_ICON, "offline tray"));
		}
		uList.clear();
		userJLst.clearSelection();
		for(ChatWindow cw : activeChats.values()){
			cw.setVisible(false);
			cw.dispose();
		}
		for(ChatWindow cw : activeChats.values()){
			cw.dispose();
		}
		activeChats.clear();
		saveConfig(user.getAlias());
		
		if(!onError){
			ServerInterface server = getServer();
			if(server != null) 
				try {
					server.logout(user.getUserId(), user.getClientId());
				} catch (RemoteException e1) {}
		} else {
			if(sysTray.getTrayIcon()!=null){
				sysTray.getTrayIcon().displayMessage("Connection Lost", "The connection to the server was lost.", TrayIcon.MessageType.INFO);
			}
		}
		
		if(updater!=null) {
			updater.interrupt();
		}
		user = null;
		setLoginVisible(true);
	}

	private void setLoginVisible(boolean vis) {
		if(vis){
			loadUserProfiles();
			setContentPane(loginPanel);
		}
		else setContentPane(userPanel);
		pack();
	}

	private ServerInterface getServer(){
		try {
			return (ServerInterface) Naming.lookup("rmi://" + config.getServerAddress() + ":"
					+ config.getServerPort() + "/" + config.getServerServiceName());
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	public UserConfiguration getConfig() {
		return config;
	}

	public User getUser() {
		return user.getUser();
	}

	public static void main(String[] args) {
		System.setProperty("sun.rmi.transport.tcp.responseTimeout", "45000");
		SwingUtilities.invokeLater(new Runnable(){
			public void run(){
				new IMClientGUI();
			}
		});
	}

	public void sendMessage(long receiverId, Message m) {
		ServerInterface server = getServer();
		if (server == null){
			activeChats.get(receiverId).addSystemMessage("Connection Problem, message not send");
			return;
		}
		try {
			if (server.SendMessage(receiverId, m, user.getKey())) return;
		} catch (RemoteException e) {
		}
		activeChats.get(receiverId).addSystemMessage("Connection Problem, message not send");
	}
	

	private void getNewMessages(){
		ServerInterface server = getServer();
		if(server == null) return;
		try{
			for(Message m :server.getMessage(user.getUserId(), user.getClientId())){
				User c = server.getUserById(m.getSenderId());
				createChatWindow(c);
				activeChats.get(m.getSenderId()).addRemoteMessage(m);
			}
		}catch(RemoteException e){
			e.printStackTrace();
		}
	}

	private void getUserList() {
		ServerInterface server = getServer();
		if(server == null) return;
		try{
			User selectedClnt = (User) userJLst.getSelectedValue();
			uList.clear();
			for(User c : server.getUsers()){
				uList.addElement(c);
				if(selectedClnt!=null&&selectedClnt.getUserId() == c.getUserId()) 
					userJLst.setSelectedValue(c, false);
			}
		}catch (RemoteException e){
			e.printStackTrace();
		}
	}
	
	private void addUserById(long id){
		ServerInterface server = getServer();
		if(server == null) return;
		try{
			uList.addElement(server.getUserById(id));
		} catch (RemoteException e){
			e.printStackTrace();
		}
	}

	private class NotificationProcessor implements Runnable{
		private NotificationMsg msg;
		
		public NotificationProcessor(NotificationMsg msg){
			this.msg = msg;
		}
		@Override
		public void run() {
			switch (msg.getReason()) {
				case USERSTATUS_CHANGE:
					addUserById(msg.getId());
					break;
				case NEW_USERLIST: 
					getUserList();
					break;
				case NEW_MESSAGE:
					getNewMessages();
					break;
				case SHUT_DOWN:
					logout(true);
					break;
				default: break;
			}
		}
		
	}
	
	private void processNotification(NotificationMsg msg) {
		if (msg == null) return;
		SwingUtilities.invokeLater(new NotificationProcessor(msg));
	}

	public void changeAlias(final String text) {
		SwingUtilities.invokeLater(new Runnable(){
			public void run(){
				ServerInterface server = getServer();
				if(server == null) return;
				PrivateUser tempu = new PrivateUser(getUser());
				tempu.setKey(user.getKey());
				tempu.setClientId(user.getClientId());
				tempu.setAlias(text);
				try {
					if(server.changeUserInfo(tempu)){
						String oldAlias = user.getAlias();
						user = tempu;
						userInfo.setText(user.toString());
						uList.addElement(tempu);
						saveConfig(user.getAlias());
						new File("profile/"+oldAlias+".xml").delete();
					}
				} catch (RemoteException e) {
					return;
				}
			}
		});
		
	}

}
